package com.yuyou.fn.portal.service.impl;


import com.github.wens.mybatisplus.toolkit.IdWorker;
import com.google.common.collect.Maps;
import com.yuyou.fn.common.constant.ClassLevelEnum;
import com.yuyou.fn.common.constant.SegmentNoEnum;
import com.yuyou.fn.common.util.*;
import com.yuyou.fn.educational.entity.Period;
import com.yuyou.fn.educational.service.ICampusService;
import com.yuyou.fn.educational.service.ICourseClassService;
import com.yuyou.fn.educational.service.IPeriodService;
import com.yuyou.fn.educational.service.IStudentRegService;
import com.yuyou.fn.platform.service.IAreaTeamService;
import com.yuyou.fn.portal.entity.AdConfig;
import com.yuyou.fn.portal.entity.CourseDisplayConfig;
import com.yuyou.fn.portal.entity.CourseFilterCondConfig;
import com.yuyou.fn.portal.repository.AdConfigRepository;
import com.yuyou.fn.portal.repository.CourseDisplayConfigRepository;
import com.yuyou.fn.portal.repository.CourseFilterCondConfigRepository;
import com.yuyou.fn.portal.service.IConfigService;
import com.yuyou.fn.portal.service.IOuterOrderService;
import com.yuyou.fn.portal.vo.MapReduceResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;

@Service
public class ConfigServiceImpl implements IConfigService {

    @Resource
    private IAreaTeamService areaTeamService;

    @Resource
    private AdConfigRepository adConfigRepository;

    @Resource
    private CourseDisplayConfigRepository courseDisplayConfigRepository;

    @Resource
    private IPeriodService periodService;

    @Resource
    private CourseFilterCondConfigRepository courseFilterCondConfigRepository;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private IOuterOrderService outerOrderService;


    @Override
    public AdConfig findAdConfigById(Long adId) {
        return adConfigRepository.findOne(adId);
    }

    @Override
    public List<AdConfig> findAdConfigByAreaTeamId(Long areaTeamId) {
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        if(areaTeamId != null ){
            return adConfigRepository.findByAreaTeamId(areaTeamId,sort);
        }else{
            return adConfigRepository.findAll(sort);
        }

    }

    @Override
    public List<AdConfig> findAdConfigByAreaTeamIdPagePosition(@NotNull Long areaTeamId, String page, String position) {
        return adConfigRepository.findByAreaTeamIdAndPageAndPosition(areaTeamId,page,position);
    }

    @Override
    public void addAdConfig(Long areaTeamId, String page, String position, String picUrl, String clickUrl, Principals.Principal principal) {

        Validate.notNull(areaTeamId);
        Validate.notNull(page);
        Validate.notNull(position);
        Validate.notNull(picUrl);
        Validate.notNull(clickUrl);

        AdConfig adConfig = new AdConfig();
        adConfig.setAdId(IdWorker.getId());
        adConfig.setAreaTeamId(areaTeamId);
        adConfig.setAreaTeamName(areaTeamService.findTeamNameById(areaTeamId));
        adConfig.setClientType(0);//暂时是移动H5
        adConfig.setPage(page);
        adConfig.setPosition(position);
        adConfig.setClickUrl(clickUrl);
        adConfig.setPicUrl(picUrl);
        adConfig.setCreateDate(TimeUtils.now());
        adConfig.setUpdateDate(TimeUtils.now());
        adConfig.setCreatorId(principal.id);
        adConfig.setCreatorName(principal.name);
        adConfig.setModifierId(principal.id);
        adConfig.setModifierName(principal.name);
        adConfigRepository.save(adConfig);

    }

    @Override
    public void updateAdConfig(Long adId, String page, String position, String picUrl, String clickUrl, Principals.Principal principal) {
        AdConfig adConfig = adConfigRepository.findOne(adId);
        adConfig.setPage(page);
        adConfig.setPosition(position);
        adConfig.setClickUrl(clickUrl);
        adConfig.setPicUrl(picUrl);
        adConfig.setUpdateDate(TimeUtils.now());
        adConfig.setModifierId(principal.id);
        adConfig.setModifierName(principal.name);
        adConfigRepository.save(adConfig);
    }

    @Override
    public void delAdConfig(Long adId) {
        adConfigRepository.delete(adId);
    }

    @Override
    public List<CourseDisplayConfig> findCourseDisplayByAreaTeamId(Long areaTeamId) {
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        if(areaTeamId == null ){
            return courseDisplayConfigRepository.findAll(sort);
        }else{
            return courseDisplayConfigRepository.findByAreaTeamId(areaTeamId,sort);
        }
    }

    @Override
    public CourseDisplayConfig findCourseDisplayConfig(Long areaTeamId) {
        return courseDisplayConfigRepository.findOne(areaTeamId);
    }

    @CacheEvict(value = "Cache_findCourseFilterCondConfigFromCache" ,allEntries = true)
    @Override
    public void updateCourseDisplayConfig(CourseDisplayConfig courseDisplayConfig, Principals.Principal principal) {


        Validate.notNull(courseDisplayConfig.getAreaTeamId());

        courseDisplayConfig.setAreaTeamName(areaTeamService.findTeamNameById(courseDisplayConfig.getAreaTeamId()));

        courseDisplayConfig.setCreateDate(TimeUtils.now());
        courseDisplayConfig.setUpdateDate(TimeUtils.now());
        courseDisplayConfig.setCreatorId(principal.id);
        courseDisplayConfig.setCreatorName(principal.name);
        courseDisplayConfig.setModifierId(principal.id);
        courseDisplayConfig.setModifierName(principal.name);

        CourseDisplayConfig old = courseDisplayConfigRepository.findOne(courseDisplayConfig.getAreaTeamId());

        if(old != null ){
            courseDisplayConfig.setCreateDate(old.getCreateDate());
            courseDisplayConfig.setCreatorId(old.getCreatorId());
            courseDisplayConfig.setCreatorName(old.getCreatorName());

        }

        if(courseDisplayConfig.getPeriods() != null ){
            for (CourseDisplayConfig.Period period : courseDisplayConfig.getPeriods()){
                Period p = periodService.findById(period.getPeriodId(), "periodName");
                period.setPeriodName(p.getPeriodName());
            }
        }
        courseDisplayConfigRepository.save(courseDisplayConfig);

        Threads.dispatch(()->{
            updateCourseFilterCondConfig(Arrays.asList(courseDisplayConfig.getAreaTeamId()));
        });

    }

    @Override
    public CourseDisplayConfig findCourseDisplayConfigFromCache(Long areaTeamId) {
        return findCourseDisplayConfig(areaTeamId);
    }


    @Cacheable(value = "Cache_findCourseFilterCondConfigFromCache")
    @Override
    public CourseFilterCondConfig findCourseFilterCondConfigFromCache(Long areaTeamId) {

        CourseFilterCondConfig config = courseFilterCondConfigRepository.findOne(areaTeamId);

        if(config != null && config.getPeriods() != null  ){

            for(CourseFilterCondConfig.Item item : config.getPeriods() ){
                List<Map<String,Object>> list  = (List<Map<String,Object>>) item.get("teachers") ;

                for(Map<String,Object> teacher : list ){

                    teacher.put("name2" , Utils.toPinyin((String)teacher.get("name")));

                }
            }

        }
        Collections.sort(config.getPeriods() , Comparator.comparing( o -> o.get("name").toString() , Comparator.reverseOrder() ));
        return config ;
    }


    @Override
    public void updateCourseFilterCondConfig(List<Long> areaTeamIds) {

        for(Long areaTeamId : areaTeamIds ){

            CourseDisplayConfig courseDisplayConfig = findCourseDisplayConfig(areaTeamId);

            if(courseDisplayConfig == null || courseDisplayConfig.getPeriods() == null || courseDisplayConfig.getPeriods().isEmpty() ){
                continue;
            }

            //List<Long> periodIds = BeanUtils.toList( courseDisplayConfig.getPeriods(),"periodId" );
            Query query = new Query();
            query.addCriteria(Criteria.where("areaTeamId").is( areaTeamId ));

            List< Criteria>  ors = new ArrayList<>(courseDisplayConfig.getPeriods().size() );

            for(CourseDisplayConfig.Period period : courseDisplayConfig.getPeriods() ){
                ors.add(Criteria.where("periodId").is(period.getPeriodId()).and("gradeId").in(period.getGradeIds()).and("campusId").in(period.getCampusIds()));
            }
            query.addCriteria(new Criteria().orOperator(ors.toArray(new Criteria[ors.size()])));

            MapReduceResults<MapReduceResult> mapReduceResults = mongoTemplate.mapReduce( query,
                    "outerCourseClass",
                    "   function(){\n" +
                            "        emit('period',this.periodId.toString() + ':' +this.periodName.toString());\n" +
                            "\temit('grade:',this.gradeId.toString()+':'+this.gradeName);\n" +
                            "\temit('grade:'+this.periodId.toString() ,this.gradeId.toString()+':'+this.gradeName);\n" +
                            "       \temit('subject:',this.subjectId.toString()+':'+this.subjectName);\n" +
                            "\temit('subject:'+this.periodId.toString() ,this.subjectId.toString()+':'+this.subjectName);\n" +
                            "        emit('level:',this.level+':'+this.level);\n" +
                            "\temit('level:'+this.periodId.toString() ,this.level+':'+this.level);\n" +
                            "        if(this.segmentNo){\n" +
                            "           emit('segmentNo:',this.segmentNo+':'+this.segmentNo);\n" +
                            "\t   emit('segmentNo:'+this.periodId.toString() ,this.segmentNo+':'+this.segmentNo);         \n" +
                            "        }\n" +
                            "       if(this.classDateTip){\n" +
                            "           emit('classDateTip:',this.classDateTip+':'+this.classDateTip);\n" +
                            "\t   emit('classDateTip:'+this.periodId.toString() ,this.classDateTip+':'+this.classDateTip);\n" +
                            "        }\n" +
                            "        if(this.campusId){\n" +
                            "            emit('campus:',this.campusId.toString()+':' +this.district+':'+this.campusName);\n" +
                            "\t    emit('campus:'+this.periodId.toString() ,this.campusId.toString()+':' +this.district+':'+this.campusName);\n" +
                            "        }\n" +
                            "        \n" +
                            "        if(this.teacherId){\n" +
                            "           emit('teacher:',this.teacherId.toString() +':'+this.teacherName);\n" +
                            "           emit('teacher:'+this.periodId.toString(),this.teacherId.toString()+':'+this.teacherName);\n" +
                            "        }\n" +
                            "   }",
                    "function(key, values) {\n" +
                            "       var set  = new Set();\n" +
                            "       values.forEach(function(item,it){\n" +
                            "           item.split(',').forEach(function(item2){\n" +
                            "               set.add(item2);\n" +
                            "               });\n" +
                            "           })  \n" +
                            "       return Array.from( set ).join(',');\n" +
                            "       }",
                    MapReduceResult.class );
            Iterator<MapReduceResult> rit = mapReduceResults.iterator();

            Map<String,MapReduceResult> mapReduceResultMap = Maps.newHashMap();

            while (rit.hasNext()){
                MapReduceResult mapReduceResult = rit.next();
                mapReduceResultMap.put(mapReduceResult.getId(),mapReduceResult);
            }

            CourseFilterCondConfig courseFilterCondConfig = new CourseFilterCondConfig();
            List<CourseFilterCondConfig.Item> periods = new ArrayList<>();

            MapReduceResult periodResult = mapReduceResultMap.get("period");

            if(periodResult != null  ){
                String[] items  = periodResult.getValue().split(",");
                for(String item : items ){
                    String[] split = StringUtils.split( item,":" ,2 );
                    CourseFilterCondConfig.Item period = new CourseFilterCondConfig.Item( split[1] , string(split[0]) );
                    period.put("grades",getItems( mapReduceResultMap, "grade:" + split[0],null));
                    period.put("subject",getItems( mapReduceResultMap, "subject:"+split[0],null));
                    period.put("levels",getItems( mapReduceResultMap, "level:"+split[0], ClassLevelEnum.values()) );
                    period.put("segmentNos",getItems( mapReduceResultMap, "segmentNo:"+split[0], SegmentNoEnum.values()));
                    period.put("campuses",getItems( mapReduceResultMap, "campus:"+split[0],null));
                    period.put("teachers",getItems( mapReduceResultMap, "teacher:"+split[0],null));
                    period.put("classDateTips",getItems( mapReduceResultMap, "classDateTip:"+split[0],null));
                    period.put("classTimeTips",getClassTimeItems());
                    periods.add(period);
                }

                Collections.sort(periods);

                CourseFilterCondConfig.Item period = new CourseFilterCondConfig.Item("不限",StringUtils.EMPTY);
                period.put("grades",getItems( mapReduceResultMap, "grade:",null));
                period.put("subject",getItems( mapReduceResultMap, "subject:",null));
                period.put("levels",getItems( mapReduceResultMap, "level:",ClassLevelEnum.values()));
                period.put("segmentNos",getItems( mapReduceResultMap, "segmentNo:",SegmentNoEnum.values()));
                period.put("campuses",getItems( mapReduceResultMap, "campus:",null));
                period.put("teachers",getItems( mapReduceResultMap, "teacher:",null));
                period.put("classDateTips",getItems( mapReduceResultMap, "classDateTip:",null));
                period.put("classTimeTips",getClassTimeItems());
                periods.add(0,period);

            }


            courseFilterCondConfig.setAreaTeamId(areaTeamId);
            courseFilterCondConfig.setPeriods(periods);

            courseFilterCondConfigRepository.save(courseFilterCondConfig);

        }



    }

    private List<CourseFilterCondConfig.Item> getItems(Map<String, MapReduceResult> mapReduceResultMap , String key , Enum<?>[] names  ) {
        List<CourseFilterCondConfig.Item> list = new ArrayList<>();

        MapReduceResult mapReduceResult = mapReduceResultMap.get(key);

        if(mapReduceResult != null ){
            String[] items  = mapReduceResult.getValue().split(",");
            for(String item : items ){
                item = string(item);
                String[] split = StringUtils.split( item,":" ,2 );
                list.add(new CourseFilterCondConfig.Item( names == null ? split[1] : EnumUtils.getName(names,Integer.parseInt(split[1])),split[0] ));
            }

            Collections.sort(list);
            list.add(0,new CourseFilterCondConfig.Item("不限",StringUtils.EMPTY ));
        }


        return list;
    }

    private List<CourseFilterCondConfig.Item> getClassTimeItems() {
        List<CourseFilterCondConfig.Item> list = new ArrayList<>();

        list.add(new CourseFilterCondConfig.Item( "不限", StringUtils.EMPTY ));
        list.add(new CourseFilterCondConfig.Item( "上午", Base64.encodeBase64URLSafeString("06:00-12:00".getBytes())));
        list.add(new CourseFilterCondConfig.Item( "下午", Base64.encodeBase64URLSafeString("12:00-18:00".getBytes())) );
        list.add(new CourseFilterCondConfig.Item( "晚上", Base64.encodeBase64URLSafeString("18:00-24:00".getBytes())) );

        return list;
    }

    private static String string(String src ){

        return src.replaceAll("(NumberLong\\(\"?(\\d+)\"?)\\)" ,"$2");

    }

    @Override
    public String findConfigCampusGeoLocation(long userId) {
        String userLastCampusGeoLocation=(String) redisTemplate.opsForHash().get("lastCampusGeoLocation",userId+"");
        if (StringUtils.isEmpty(userLastCampusGeoLocation)){
            userLastCampusGeoLocation=outerOrderService.saveLastCampusGeoLocation(userId);
        }
        return userLastCampusGeoLocation;
    }
}
